package com.bpmn.utils;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.bpmn.common.utils.TenantUtils;
import lombok.SneakyThrows;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.TaskService;
import org.flowable.task.api.Task;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;

import java.util.Date;
import java.util.Objects;
import java.util.concurrent.Callable;

/**
 * Flowable 相关的工具方法
 *
 * @author rain
 */
public class FlowableUtils {

    private static final ProcessEngine PROCESS_ENGINE = SpringUtil.getBean(ProcessEngine.class);

    public static void setAuthenticatedUserId(String userId) {
        Authentication.setAuthenticatedUserId(userId);
    }

    public static void clearAuthenticatedUserId() {
        Authentication.setAuthenticatedUserId(null);
    }

    public static <V> V executeAuthenticatedUserId(String userId, Callable<V> callable) {
        setAuthenticatedUserId(userId);
        try {
            return callable.call();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            clearAuthenticatedUserId();
        }
    }

    @SneakyThrows
    public static <V> V execute(String tenantId, Callable<V> callable) {
        if (ObjectUtil.isEmpty(tenantId)
                || Objects.equals(tenantId, ProcessEngineConfiguration.NO_TENANT_ID)) {
            return callable.call();
        } else {
            return TenantUtils.execute(tenantId, callable);
        }
    }

    /**
     * 格式化多实例（并签、或签）的 collectionVariable 变量（多实例对应的多审批人列表）
     *
     * @param activityId 活动编号
     * @return collectionVariable 变量
     */
    public static String formatExecutionCollectionVariable(String activityId) {
        return activityId + "_assignees";
    }

    /**
     * 格式化多实例（并签、或签）的 collectionElementVariable 变量（当前实例对应的一个审批人）
     *
     * @param activityId 活动编号
     * @return collectionElementVariable 变量
     */
    public static String formatExecutionCollectionElementVariable(String activityId) {
        return activityId + "_assignee";
    }

    /**
     * 获得任务根任务的父任务编号
     *
     * @param task 任务
     * @return 根任务的父任务编号
     */
    public static String getTaskRootParentId(Task task) {
        if (task == null || task.getParentTaskId() == null) {
            return null;
        }
        for (int i = 0; i < Short.MAX_VALUE; i++) {
            Task parentTask = QueryUtils.taskQuery().taskId(task.getParentTaskId()).singleResult();
            if (parentTask == null) {
                return null;
            }
            if (parentTask.getParentTaskId() == null) {
                return parentTask.getId();
            }
            task = parentTask;
        }
        throw new IllegalArgumentException(String.format("Task(%s) 层级过深，无法获取父节点编号", task.getId()));
    }

    /**
     * 创建子任务
     */
    public static Task createSubTask(TaskEntity parentTask, String assignee) {
        return createSubTask(parentTask, parentTask.getId(), assignee);
    }

    /**
     * 创建子任务
     */
    public static Task createSubTask(TaskEntity parentTask, String parentTaskId, String assignee) {
        TaskService taskService = PROCESS_ENGINE.getTaskService();
        TaskEntity task = (TaskEntity) taskService.newTask(IdUtil.fastSimpleUUID());
        task.setAssignee(assignee);
        copyToNewTask(parentTask, parentTaskId, task);
        taskService.saveTask(task);
        return task;
    }

    /**
     * 创建子任务对象
     */
    public static Task createNewSubTask(TaskEntity parentTask, String parentTaskId) {
        TaskService taskService = PROCESS_ENGINE.getTaskService();
        TaskEntity task = (TaskEntity) taskService.newTask(IdUtil.fastSimpleUUID());
        copyToNewTask(parentTask, parentTaskId, task);
        return task;
    }

    private static void copyToNewTask(TaskEntity parentTask, String parentTaskId, TaskEntity task) {
        task.setProcessInstanceId(parentTask.getProcessInstanceId());
        task.setProcessDefinitionId(parentTask.getProcessDefinitionId());
        task.setParentTaskId(parentTaskId);
        task.setCategory(parentTask.getCategory());
        task.setDescription(parentTask.getDescription());
        task.setName(parentTask.getName());
        task.setTaskDefinitionKey(parentTask.getTaskDefinitionKey());
        task.setTaskDefinitionId(parentTask.getTaskDefinitionId());
        task.setTenantId(parentTask.getTenantId());
        task.setCreateTime(new Date());
        task.setPriority(parentTask.getPriority());
        task.setScopeType(parentTask.getScopeType());
    }


}
